package test.serverframe.armc.server.manager.service.impl;

import org.apache.commons.lang.StringUtils;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.TriggerKey;
import org.quartz.utils.Key;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.microcore.dao.mapper.InvokeTriggerMapper;
import com.microcore.entity.InvokeTrigger;
import com.microcore.entity.UpdateTrigger;
import com.microcore.entity.param.TriggerParams;
import com.microcore.service.JobService;
import com.microcore.util.DateUtil;

import test.serverframe.armc.server.manager.controller.vo.TaskInvokeVo;
import test.serverframe.armc.server.manager.dao.mapper.*;
import test.serverframe.armc.server.manager.domain.*;
import test.serverframe.armc.server.manager.domain.params.AddTaskInvokeParams;
import test.serverframe.armc.server.manager.domain.params.UpdateInvokeStatusParams;
import test.serverframe.armc.server.manager.service.exec.Constants;
import test.serverframe.armc.server.util.JsonUtil;
import test.serverframe.armc.server.manager.domain.params.AddTaskEnterInvokeParams;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.validation.Valid;

/**
 * @Description 任务调度接口实现类
 * @Author LSY
 * @Date 2018/10/10 14:48
 * @ClassName test.serverframe.armc.server.manager.service.impl.TaskInvokeServiceImpl
 */
@Service("taskInvokeService")
public class TaskInvokeServiceImpl extends BaseServiceImpl<TaskInvoke, String>  {

    @Autowired
    private TaskInvokeMapper taskInvokeMapper;
    @Autowired
    private TaskEnterInvokeMapper taskEnterInvokeMapper;
    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private InvokeEnterTagMapper invokeEnterTagMapper;
    @Autowired
    private InvokeTriggerMapper triggerMapper;
    @Autowired
    private SchedulerFactoryBean factory;
    @Autowired
    private JobService jobService;

    @Override
    protected String getMapperName() {
        return TaskInvokeMapper.class.getName();
    }


    @Transactional(rollbackFor = Exception.class)
    public Integer addTaskInvoke(AddTaskInvokeParams params) throws Exception {
    	
    	String taskInvokeId = UUID.randomUUID().toString();
        //添加调度
        addTaskInvoke(params, taskInvokeId);
        
        //获取标签id集合 修改by UaenaSA
        List<String> tagIds=new ArrayList<>();
        if (params.getTags()!=null){
        for (Tag tag:params.getTags()){
            tagIds.add(tag.getId());
        }
        //遍历集合并新增调度标签关联
        for (String tagId : tagIds) {
            InvokeEnterTag invokeEnterTag = new InvokeEnterTag();
            invokeEnterTag.setId(UUID.randomUUID().toString());
            invokeEnterTag.setInvokeId(taskInvokeId);
            invokeEnterTag.setTagId(tagId);
            invokeEnterTagMapper.insert(invokeEnterTag);
        }}
        
        //添加任务关联调度
        addTaskEnterInvoke(params.getTaskRelateInvokes(), taskInvokeId);
        
        //添加调度计划
        addTriggers(taskInvokeId, params);
        return 1;
    }

    private void addTaskInvoke(AddTaskInvokeParams params, String taskInvokeId) throws Exception {
		
    	TaskInvoke taskInvoke = new TaskInvoke();
    	taskInvoke.setUpdataTime(new Date());
        taskInvoke.setTaskInvokeId(taskInvokeId);
        taskInvoke.setClassName(Constants.REFLECTION_CLASS_NAME);
    	taskInvoke.setMethodName(Constants.REFLECTION_METHOD_NAME);
    	taskInvoke.setRemark(params.getRemark());
    	taskInvoke.setInvokeName(params.getInvokeName());
//    	taskInvoke.setParams(getParams(taskInvokeId));
    	taskInvoke.setStatus(0);
        taskInvoke.setUsed(true);
        taskInvoke.setFailedStrategy(params.getFailedStrategy());
        taskInvoke.setConcurrent(params.isConcurrent());
        taskInvoke.setExecutePlanData(params.getExecutePlanData());
        taskInvoke.setNodeArr(params.getNodeArr());
        taskInvokeMapper.insertSelective(taskInvoke);
	}
    
    private String getParams(String taskInvokeId) {
    	return "[{\"key\":\"fkId\",\"value\":\""+taskInvokeId+"\"},{\"key\":\"isReStart\",\"value\":\"false\"}]";
    }


    public Integer addTaskEnterInvoke(List<AddTaskEnterInvokeParams> taskDefineList, String taskInvokeId) {
        if (taskDefineList != null && taskDefineList.size() > 0) {
        	// 任务关联对象集合
            List<TaskEnterInvoke> taskEnterInvokes = new ArrayList<>();
            for (AddTaskEnterInvokeParams taskDefine : taskDefineList) {
                // 添加任务关联任务调度
                TaskEnterInvoke taskEnterInvoke = new TaskEnterInvoke();
                taskEnterInvoke.setTaskEnterInvokeId(UUID.randomUUID().toString());
                taskEnterInvoke.setTaskId(taskDefine.getTaskId());
                taskEnterInvoke.setTaskInvokeId(taskInvokeId);
                taskEnterInvoke.setTaskDelay(taskDefine.getTaskDelay());//给当前调度的每个人任务添加执行间隔时间
                taskEnterInvoke.setTaskParentIds(JsonUtil.parseString(taskDefine.getParentIds()));
                taskEnterInvoke.setTaskChildIds(JsonUtil.parseString(taskDefine.getChildIds()));
                //taskEnterInvoke.setTaskOrder(taskDefine.getTaskOrder());//给当前调度的每个人任务添加执行顺序
                taskEnterInvokes.add(taskEnterInvoke);
            }
            // 批量添加关联任务集合
            return taskEnterInvokeMapper.insertCollectList(taskEnterInvokes);
        }
        return null;
    }
    
	/*
     * 给调度添加多个trigger
	 * @author yegui  
	 * @date 2019年1月4日  
	 * @param triggers
	 * void
	 */  
	private void addTriggers(String taskInvokeId, AddTaskInvokeParams params) throws Exception {
		
		for (TriggerParams trigger : params.getTriggers()) {
			addTrigger(trigger, taskInvokeId);
		}
		/*//1.获得开始时间到结束时间的日期list
		List<String> dateList = DateUtil.findDaysStr(params.getStartDatetime(), params.getEndDatetime());
		//2.移除list中特殊计划的日期
		for (TriggerParams invokeTrigger : params.getTriggers()) {
			if (invokeTrigger.isSpecial()) {
				String specialDate = DateUtil.onlyDateformat(DateUtil.onlyDateformat(invokeTrigger.getStartTime()));
				dateList.remove(specialDate);
				addTrigger(invokeTrigger, taskInvokeId, DateUtil.formatStr(invokeTrigger.getStartTime()), 
	        			DateUtil.formatStr(invokeTrigger.getEndTime()));
			}
		}
		//3.将通用计划的日期和时间合并起来
		for (String dateStr : dateList) {
			for (TriggerParams invokeTrigger : params.getTriggers()) {
				if (!invokeTrigger.isSpecial()) {
					Date startDateTime = DateUtil.formatStr(dateStr + invokeTrigger.getStartTime());
					Date endDateTime = DateUtil.formatStr(dateStr + invokeTrigger.getEndTime());
					addTrigger(invokeTrigger, taskInvokeId, startDateTime, endDateTime);
				}
			}
		}*/
		
	}
    
    private void addTrigger(TriggerParams invokeTrigger, String taskInvokeId) throws Exception {
    	String triggerId = UUID.randomUUID().toString();
    	InvokeTrigger trigger = new InvokeTrigger();
    	trigger.setFkId(taskInvokeId);
    	trigger.setInvokeTriggerId(triggerId);
    	trigger.setStartDatetime(DateUtil.formatStr(invokeTrigger.getStartTime()));
    	trigger.setEndDatetime(DateUtil.formatStr(invokeTrigger.getEndTime()));
    	trigger.setCount(invokeTrigger.getCount());
    	trigger.setInterval(invokeTrigger.getInterval());
    	trigger.setParams(getParams(taskInvokeId));
    	String uuid = UUID.randomUUID().toString();
    	if (invokeTrigger.isSpecial()) {
    		trigger.setTriggerName("#" + uuid);
    	} else {
    		trigger.setTriggerName(uuid);
    	}
//    	trigger.setStartTime(invokeTrigger.getStartTime());
//    	trigger.setEndTime(invokeTrigger.getEndTime());
    	triggerMapper.insertSelective(trigger);
		
	}


	@Transactional(rollbackFor = Exception.class)
    public Integer updateTaskInvoke(AddTaskInvokeParams params) throws Exception {
        if (StringUtils.isEmpty(params.getTaskInvokeId())) {
            throw new Exception("任务调度id为空！");
        }
        
        //修改调度
        updateInvoke(params);
        //更新trigger
        updateTrigger(params);
        //更新任务关联调度
        updateTaskRelateInvoke(params);
        //删除所有调度关联标签重新新增
        invokeEnterTagMapper.deleteByTaskInvokeId(params.getTaskInvokeId());
        //重新新增了调度标签关联
        List<String> tagIds=new ArrayList<>();
        for (Tag tag:params.getTags()){
            tagIds.add(tag.getId());
        }
        for (String tagId : tagIds) {
            InvokeEnterTag invokeEnterTag = new InvokeEnterTag();
            invokeEnterTag.setId(UUID.randomUUID().toString());
            invokeEnterTag.setInvokeId(params.getTaskInvokeId());
            invokeEnterTag.setTagId(tagId);
            invokeEnterTagMapper.insert(invokeEnterTag);
        }

        return 1;
    }

    private void updateTrigger(AddTaskInvokeParams params) throws Exception {
    	//删除所有trigger表里的trigger
    	triggerMapper.deleteByFkId(params.getTaskInvokeId());
    	//移除scheduler里的triggerkey
    	jobService.deleteTriggerKey(params.getTaskInvokeId());
    	//insert Trigger
    	for (TriggerParams trigger : params.getTriggers()) {
    		String triggerId = UUID.randomUUID().toString();
    		InvokeTrigger invokeTrigger = new InvokeTrigger();
    		invokeTrigger.setCount(trigger.getCount());
    		invokeTrigger.setEndDatetime(DateUtil.formatStr(trigger.getEndTime()));
    		invokeTrigger.setInterval(trigger.getInterval());
    		invokeTrigger.setInvokeTriggerId(triggerId);
    		invokeTrigger.setStartDatetime(DateUtil.formatStr(trigger.getStartTime()));
    		invokeTrigger.setFkId(params.getTaskInvokeId());
    		invokeTrigger.setParams(getParams(params.getTaskInvokeId()));
    		String uuid = UUID.randomUUID().toString();
        	if (trigger.isSpecial()) {
        		invokeTrigger.setTriggerName("#" + uuid);
        	} else {
        		invokeTrigger.setTriggerName(uuid);
        	}
    		triggerMapper.insertSelective(invokeTrigger);
    		
    		//更新quartz的trigger
    		TaskInvoke invoke = taskInvokeMapper.selectByPrimaryKey(params.getTaskInvokeId());
    		UpdateTrigger update = new UpdateTrigger();
    		update.setClassName(invoke.getClassName());
    		update.setMethodName(invoke.getMethodName());
    		update.setConcurrent(invoke.isConcurrent());
    		update.setInvokeTrigger(invokeTrigger);
    		jobService.updateTriggerKey(update);
    	}
	}


	private void updateTaskRelateInvoke(AddTaskInvokeParams params) {
    	//删除所有任务关联任务调度重新新增
        taskEnterInvokeMapper.deleteByTaskInvokeId(params.getTaskInvokeId());
        //添加调度下的所有任务
        addTaskEnterInvoke(params.getTaskRelateInvokes(), params.getTaskInvokeId());
	}


	private void updateInvoke(AddTaskInvokeParams params) {
    	TaskInvoke invoke = new TaskInvoke();
    	invoke.setUpdataTime(new Date());
    	invoke.setTaskInvokeId(params.getTaskInvokeId());
    	invoke.setRemark(params.getRemark());
    	invoke.setInvokeName(params.getInvokeName());
    	invoke.setFailedStrategy(params.getFailedStrategy());
    	invoke.setConcurrent(params.isConcurrent());
    	invoke.setNodeArr(params.getNodeArr());
    	invoke.setExecutePlanData(params.getExecutePlanData());
    	//修改 修改时间
    	invoke.setUpdataTime(new Date());
    	taskInvokeMapper.updateByPrimaryKeySelective(invoke);
	}

    public int deleteInvoke(@Valid List<String> taskInvokeIds) throws Exception {
    	for (String id : taskInvokeIds) {
    		TaskInvoke invoke = new TaskInvoke();
    		invoke.setUsed(false);
    		invoke.setTaskInvokeId(id);
    		taskInvokeMapper.updateByPrimaryKeySelective(invoke);
			Scheduler scheduler = factory.getScheduler();
			TriggerKey triggerKey = TriggerKey.triggerKey(id, Key.DEFAULT_GROUP);
			scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            scheduler.deleteJob(JobKey.jobKey(id, Key.DEFAULT_GROUP));// 删除任务
    	}
        return 1;
    }

    public TaskInvokeVo findById(String taskInvokeId) {
        return taskInvokeMapper.modifyByTaskInvokeId(taskInvokeId) ;
    }

    public Task findByTaskId(String taskId) {
        return taskMapper.selectByPrimaryKey(taskId);
    }

	public TaskInvoke selectSelective(TaskInvoke taskInvoke) {
		return taskInvokeMapper.selectSelective(taskInvoke);
	}
	
	public void batchUpdateStatus(List<UpdateInvokeStatusParams> paramList) throws Exception {
		Scheduler scheduler = factory.getScheduler();
		for (UpdateInvokeStatusParams param : paramList) {
			modifyStatus(param.getStatus(), param.getTaskInvokeId());
			//TaskInvoke taskInvoke = taskInvokeMapper.selectByPrimaryKey(param.getTaskInvokeId());
			List<InvokeTrigger> triggers = triggerMapper.getTriggersByInvokeId(param.getTaskInvokeId());
			JobKey jobKey = null;
			for (InvokeTrigger trigger : triggers) {
				jobKey = JobKey.jobKey(trigger.getInvokeTriggerId(), Key.DEFAULT_GROUP);
				//如果用户需要改变调度状态，从数据库中查询调度的状态，
	            if (param.getStatus() == Constants.INVOKE_WAITING) {
	        		TaskInvoke concurrentInvoke = taskInvokeMapper.selectByPrimaryKey(param.getTaskInvokeId());
	            	jobService.startInvoke(scheduler, concurrentInvoke.getClassName(), concurrentInvoke.getMethodName(), 
	            			concurrentInvoke.isConcurrent(), false, concurrentInvoke.getTaskInvokeId());
	            	modifyStatus(Constants.INVOKE_EXECUTING, param.getTaskInvokeId());
	            	
	            } else if (param.getStatus() == Constants.INVOKE_EXECUTING) {
	    			scheduler.pauseJob(jobKey);
	    			modifyStatus(Constants.INVOKE_PAUSING, param.getTaskInvokeId());
	    			
	            } else if (param.getStatus() == Constants.INVOKE_PAUSING) {
	            	scheduler.resumeJob(jobKey);
	            	modifyStatus(Constants.INVOKE_EXECUTING, param.getTaskInvokeId());
	            }
			}
        }
	}

	private void modifyStatus(int status, String taskInvokeId) {
		TaskInvoke invoke = new TaskInvoke();
		invoke.setStatus(status);
		invoke.setTaskInvokeId(taskInvokeId);
		taskInvokeMapper.updateByPrimaryKeySelective(invoke);
	}

}
